<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="../src/css/default.css">
    <link rel="stylesheet" href="./css/style.css">
    <title>作用域的问题</title>
</head>

<body>

    <script src="./js/index.js"></script>

    <!-- 
        作用域：前端第一大难点，重点
            什么是作用域？ 就是变量生效的范围；
        
            1.量是怎么声明的？
                es5:
                    var 变量 终将被淘汰的声明方式；因为它有缺陷:

                    (1).变量提升:
                        变量声明可以放在使用之后，不符合预期语法；

                        console.log(a) //  undefined 
                        var a = 3

                        按理说上面应该报错 结果声明被提前了
                        等价于：
                        var a
                        console.log(a)
                        a = 3

                        可能会扰乱正常的作用域；
                    (2).重复声明变量 造成变量覆盖
                        变量名可以被重复声明，比如函数名跟变量名同名，必然会覆盖掉前面的那个；

                        var foo = 1
                        var foo = function (){}

                        foo 就是函数 变量1被覆盖了...

                es6:
                    let 变量：
                        用于解决var遗留的缺陷；目前，比较完美的变量声明方式；
                    const 常量：
                        不会变化的量 优点：不允许直接修改 保证这个量的安全性；
                        规范：
                            常量名一般全部大写；
                            一定要初始化常量；
            2.函数的声明：
                function foo(){} 函数是js一等功臣； 它具有最高优先级，会把整个函数提升到代码最前面等待执行；
            3.作用域：
                让变量在有限的范围内被使用；

                变量的访问顺序：就近原则，都是惰性的嘛； 从内到外...内部没有再向上一层去找...如果全局都没有，则报错；

                (1).全局作用域：
                    直接写在最外面的变量 所在的区域；
                        比如在一个script标签内或一个js文件内的任意位置，都可以被访问的变量；
                        var 的变量提升 甚至会突破 这个全局：
                            只要在同一个窗口下运行的不同的js文件，都可以访问被被任意文件的 var 声明的变量；
                            变量就存在安全隐患；

                (2).函数作用域：
                    也就是局部作用域，在函数内部声明的变量，仅在该函数内部才能访问到；
                    也因为这种特性 所以有了闭包这种东西，后续再说；

                    其实var的变量提升，就是提升到函数作用域的顶层；

                    function foo(a){} ... 函数的形参 实际上是隐式声明在了函数内部，
                    然后在内部赋上外部传入的实参，再进行运算；

                (3).块级作用域：
                    if(){} while(){} do{} for(){} switch(){}...在这些区域内部声明的变量，只能在这些区域内被访问；
                    这就是块级作用域；

            4.引用类型：
                对象就是一种引用类型，它是一种动态的数据类型的集合，本身就是一种数据结构；
                它不同于其他基本数据类型，基本数据类型存放在栈内存里面，而对象的数据都存放在堆内存里；

                栈内存里存放的，只是对象的一个引用名称而已；

                所以，对对象的直接复制操作，其实只是浅拷贝：
                    let obj = {a:2}
                    let b = obj  相当于给obj 加了个别名 b 
                    b.a = 3 <===> obj.a = 3
                    两者的操作都会影响同一个对象；

                如何实现对象的深拷贝？
                    其实就是创建一个新对象，然后遍历老对象所有属性，一一复制给新对象即可；
                    这样 把这个新对象复制给某个变量名，对它进行操作就不会影响源对象了；

                    具体实现方法：
                    (1).JSON方法：
                        JSON.stringify() 和 JSON.parse()

                        先把源对象转化为字符串 然后重新合成一个新对象；
                        let b = JSON.parse(JSON.stringify(a))

                        缺点：无法拷贝undefined、function、RegExp等类型；

                    (2).Object.assign(目标对象，源对象)
                        这个方法是把两个对象进行合并，返回一个新的对象，并不会改变源对象；

                        let b = Object.assign({},a)

                        缺点：只能拷贝单层对象，对于对象嵌套，或者多层引用类型，就无法深拷贝了
                        比如：
                        let obj = {
                            a:1,
                            b:[1,2,3],
                            C:{
                                x:[4,5,6]
                            }
                        }
                        这样的嵌套类型，就无法深拷贝；

                    (3).相对完美的方式： 递归法 遍历拷贝；
                        对象的遍历使用 for...in
                        let aaa = {
                            x: 1,
                            y: 2,
                            z: 3
                        }

                        for (const key in aaa) {
                            检测一下 可遍历属性是否存在
                            if (Object.hasOwnProperty.call(aaa, key)) {
                                console.log(aaa[key])
                            }
                        }


     -->
</body>

</html>